#pragma once

#include "Option.h"
#include "BooleanOption.h"

#include <QHash>

namespace commandline
{

	class Parser : public QObject
	{
	protected:
		QHash<QString, Option*> _options;
		QString _errorText;
		/* No public inheritance because we need to modify a few methods */
		QCommandLineParser _parser;

		QStringList _getNames(const char shortOption, const QString& longOption);
		QString _getDescription(const QString& description, const QString& default_ = QString());

	public:
		~Parser() override;

		bool parse(const QStringList& arguments);
		void process(const QStringList& arguments);
		void process(const QCoreApplication& app);
		QString errorText() const;

		template<class OptionT, class ... Args>
		OptionT& add(
			const char shortOption,
			const QString longOption,
			const QString description,
			const QString default_,
			Args ... args)
		{
			OptionT* option = new OptionT(
				_getNames(shortOption, longOption),
				_getDescription(description, default_),
				longOption,
				default_,
				args...);
			addOption(option);
			return *option;
		}

		/* gcc does not support default arguments for variadic templates which
		 * makes this method necessary */
		template<class OptionT>
		OptionT& add(
			const char shortOption,
			const QString longOption,
			const QString description,
			const QString default_ = QString())
		{
			OptionT* option = new OptionT(
				_getNames(shortOption, longOption),
				_getDescription(description, default_),
				longOption,
				default_);
			addOption(option);
			return *option;
		}

		template<class OptionT>
		OptionT& addHidden(
			const char shortOption,
			const QString longOption,
			const QString description,
			const QString default_ = QString())
		{
			OptionT* option = new OptionT(
				_getNames(shortOption, longOption),
				_getDescription(description, default_),
				longOption,
				default_);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
			option->setFlags(QCommandLineOption::HiddenFromHelp);
#else
			option->setHidden(true);
#endif

			addOption(option);
			return *option;
		}

		Parser(const QString& description = QString())
		{
			if (description.size())
				setApplicationDescription(description);
		}

		QCommandLineOption addHelpOption()
		{
			return _parser.addHelpOption();
		}

		bool addOption(Option& option);
		bool addOption(Option* option);
		void addPositionalArgument(const QString& name, const QString& description, const QString& syntax = QString())
		{
			_parser.addPositionalArgument(name, description, syntax);
		}

		QCommandLineOption addVersionOption()
		{
			return _parser.addVersionOption();
		}

		QString applicationDescription() const
		{
			return _parser.applicationDescription();
		}

		void clearPositionalArguments()
		{
			_parser.clearPositionalArguments();
		}

		QString helpText() const
		{
			return _parser.helpText();
		}

		bool isSet(const QString& name) const
		{
			return _parser.isSet(name);
		}

		bool isSet(const Option& option) const
		{
			return _parser.isSet(option);
		}

		bool isSet(const Option* option) const
		{
			return _parser.isSet(*option);
		}

		QStringList optionNames() const
		{
			return _parser.optionNames();
		}

		QStringList positionalArguments() const
		{
			return _parser.positionalArguments();
		}

		void setApplicationDescription(const QString& description)
		{
			_parser.setApplicationDescription(description);
		}

		void setSingleDashWordOptionMode(QCommandLineParser::SingleDashWordOptionMode singleDashWordOptionMode)
		{
			_parser.setSingleDashWordOptionMode(singleDashWordOptionMode);
		}

		[[ noreturn ]] void showHelp(int exitCode = 0)
		{
			_parser.showHelp(exitCode);
		}

		QStringList unknownOptionNames() const
		{
			return _parser.unknownOptionNames();
		}

		QString value(const QString& optionName) const
		{
			return _parser.value(optionName);
		}

		QString value(const Option& option) const
		{
			return _parser.value(option);
		}

		QStringList values(const QString& optionName) const
		{
			return _parser.values(optionName);
		}

		QStringList values(const Option& option) const
		{
			return _parser.values(option);
		}
	};

}
